/*****************************************************************************
* flv.c: flv muxer
*****************************************************************************
* Copyright (C) 2009-2011 x264 project
*
* Authors: Kieran Kunhya <kieran@kunhya.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
*
* This program is also available under a commercial proprietary license.
* For more information, contact us at licensing@x264.com.
*****************************************************************************/

//#include "typedef.h"
#include "flv.h"
#include <malloc.h>
#include <string.h>

#define BUFSIZE (1024 * 1024)       //  1M
unsigned char buffer[BUFSIZE];
int datasize;
int pos = 0;
int prepos = 0;

int FillBuffer(char *filename)
{
	FILE *fp = 0;
	int size;

	fp = fopen(filename, "rb");
	if (fp == 0)
	{
		printf("Failed to open %s\n", filename);
		return -1;
	}
	fseek(fp, 0, SEEK_SET);
	size = fread(buffer, sizeof(unsigned char), BUFSIZE, fp);
	if (size >= BUFSIZE)
	{
		printf("File size is larger than BUFSIZE\n");
	}
	else printf("File size is smaller than BUFSIZE\n");

	if (!fp)
		fclose(fp);
	datasize = size;
	printf("datasize: %d\n", datasize);
	return size;

}

int InitFirstPos(rtpbuf_t *rtpReadbuf)
{
	while (rtpReadbuf->bufpos < rtpReadbuf->bufsize - 4)
	{
		if (rtpReadbuf->buf[pos] == 0x00)
		if (rtpReadbuf->buf[pos + 1] == 0x00)
		if (rtpReadbuf->buf[pos + 2] == 0x00)
		if (rtpReadbuf->buf[pos + 3] == 0x01)
			break;
		rtpReadbuf->bufpos++;
	}
	printf("pos: %d\n", rtpReadbuf->bufpos );
	rtpReadbuf->bufnextpos = rtpReadbuf->bufpos;
	return 0;
}

int GetNalFromBuffer(x264_nal_t *nalPacket)
{
	pos = prepos + 4;
	while (pos < datasize - 4)
	{
		if (buffer[pos] == 0x00)
		if (buffer[pos + 1] == 0x00)
		if (buffer[pos + 2] == 0x00)
		if (buffer[pos + 3] == 0x01)
			break;
		pos++;
	}
	if (pos == datasize - 4)
	{
		printf("Consume all the datas!\n");

		pos = datasize;
		printf("pos: %d\n", pos);
		nalPacket->i_payload = pos - prepos;
		nalPacket->p_payload = &buffer[prepos];
		nalPacket->i_type = nalPacket->p_payload[4] & ((1 << 5) - 1);
		return -1;
	}

	printf("pos: %d\n", pos);
	nalPacket->i_payload = pos - prepos;
	nalPacket->p_payload = &buffer[prepos];
	nalPacket->i_type = nalPacket->p_payload[4] & ((1 << 5) - 1);
	prepos = pos;

	return 0;
}

int write_header(flv_buffer *c)
{
	x264_put_tag(c, "FLV"); // Signature
	x264_put_byte(c, 1);    // Version
	x264_put_byte(c, 1);    // Video Only
	x264_put_be32(c, 9);    // DataOffset
	x264_put_be32(c, 0);    // PreviousTagSize0

	return flv_flush_data(c);
}

int open_file(char *psz_filename, flv_hnd_t **p_handle)
{
	flv_hnd_t *p_flv = (flv_hnd_t *)malloc(sizeof(flv_hnd_t));
	//   *p_handle = NULL;
	if (!p_flv)
		return -1;
	memset(p_flv, 0, sizeof(*p_flv));

	//    p_flv->b_dts_compress = opt->use_dts_compress;

	p_flv->c = flv_create_writer(psz_filename);
	if (!p_flv->c)
		return -1;

	CHECK(write_header(p_flv->c));
	*p_handle = p_flv;
	printf("p_flv:%ld\n", p_flv);

	return 0;
}

int set_script_param(flv_hnd_t *handle, double i_width, double i_height)
{
	flv_hnd_t *p_flv = handle;
	flv_buffer *c = p_flv->c;
	printf("set_script_param 1\n");
	x264_put_byte(c, FLV_TAG_TYPE_META); // Tag Type "script data"

	int start = c->d_cur;
	x264_put_be24(c, 0); // data length
	p_flv->i_firsttimestamp_pos = c->d_cur + c->d_total + 1;
	x264_put_be24(c, 0); // timestamp
	x264_put_be32(c, 0); // reserved
	printf("set_script_param 2\n");
	x264_put_byte(c, AMF_DATA_TYPE_STRING);
	x264_put_amf_string(c, "onMetaData");
	printf("set_script_param 3\n");
	x264_put_byte(c, AMF_DATA_TYPE_MIXEDARRAY);
	x264_put_be32(c, 7);

	x264_put_amf_string(c, "width");
	x264_put_amf_double(c, i_width);

	x264_put_amf_string(c, "height");
	x264_put_amf_double(c, i_height);

	x264_put_amf_string(c, "framerate");

	p_flv->i_framerate_pos = c->d_cur + c->d_total + 1;
	x264_put_amf_double(c, 0); // written at end of encoding
	/*
	if( !p_param->b_vfr_input )
	x264_put_amf_double( c, (double)p_param->i_fps_num / p_param->i_fps_den );
	else
	{
	p_flv->i_framerate_pos = c->d_cur + c->d_total + 1;
	x264_put_amf_double( c, 0 ); // written at end of encoding
	}
	*/
	x264_put_amf_string(c, "videocodecid");
	x264_put_amf_double(c, FLV_CODECID_H264);

	x264_put_amf_string(c, "duration");
	p_flv->i_duration_pos = c->d_cur + c->d_total + 1;
	x264_put_amf_double(c, 1.64); // written at end of encoding

	x264_put_amf_string(c, "filesize");
	p_flv->i_filesize_pos = c->d_cur + c->d_total + 1;
	x264_put_amf_double(c, 0); // written at end of encoding

	x264_put_amf_string(c, "videodatarate");
	p_flv->i_bitrate_pos = c->d_cur + c->d_total + 1;
	x264_put_amf_double(c, 384.00); // written at end of encoding

	x264_put_amf_string(c, "");
	x264_put_byte(c, AMF_END_OF_OBJECT);

	unsigned length = c->d_cur - start;
	rewrite_amf_be24(c, length - 10, start);

	x264_put_be32(c, length + 1); // tag length
	/*
	p_flv->i_fps_num = p_param->i_fps_num;
	p_flv->i_fps_den = p_param->i_fps_den;
	p_flv->d_timebase = (double)p_param->i_timebase_num / p_param->i_timebase_den;
	p_flv->b_vfr_input = p_param->b_vfr_input;
	p_flv->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
	*/
	return 0;
}

int write_h264_headers(flv_hnd_t *handle, x264_nal_t *p_nal)
{
	flv_hnd_t *p_flv = handle;
	flv_buffer *c = p_flv->c;

	int sps_size = p_nal[0].i_payload;
	int pps_size = p_nal[1].i_payload;
	//   int sei_size = p_nal[2].i_payload;

	// SEI
	/* It is within the spec to write this as-is but for
	* mplayer/ffmpeg playback this is deferred until before the first frame */
	/*
	p_flv->sei = malloc( sei_size );
	if( !p_flv->sei )
	return -1;
	p_flv->sei_len = sei_size;

	memcpy( p_flv->sei, p_nal[2].p_payload, sei_size );
	*/
	// SPS
	uint8_t *sps = p_nal[0].p_payload ;

	x264_put_byte(c, FLV_TAG_TYPE_VIDEO);
	x264_put_be24(c, 0); // rewrite later
	x264_put_be24(c, 0); // timestamp
	x264_put_byte(c, 0); // timestamp extended
	x264_put_be24(c, 0); // StreamID - Always 0
	p_flv->start = c->d_cur; // needed for overwriting length

	x264_put_byte(c, 7 | FLV_FRAME_KEY); // Frametype and CodecID
	x264_put_byte(c, 0); // AVC sequence header
	x264_put_be24(c, 0); // composition time

	x264_put_byte(c, 1);      // version
	x264_put_byte(c, sps[1]); // profile
	x264_put_byte(c, sps[2]); // profile
	x264_put_byte(c, sps[3]); // level
	x264_put_byte(c, 0xff);   // 6 bits reserved (111111) + 2 bits nal size length - 1 (11)
	x264_put_byte(c, 0xe1);   // 3 bits reserved (111) + 5 bits number of sps (00001)

	x264_put_be16(c, sps_size );
	flv_append_data(c, sps, sps_size );

	// PPS
	x264_put_byte(c, 1); // number of pps
	x264_put_be16(c, pps_size );
	flv_append_data(c, p_nal[1].p_payload , pps_size );

	// rewrite data length info
	unsigned length = c->d_cur - p_flv->start;
	rewrite_amf_be24(c, length, p_flv->start - 10);
	x264_put_be32(c, length + 11); // Last tag size
	CHECK(flv_flush_data(c));

	return sps_size + pps_size;
}

int write_h264_frame(rtp2flvCtx_t *p_rtp,flv_hnd_t *handle, x264_nal_t nalu)
{
	flv_hnd_t *p_flv = handle;
	flv_buffer *c = p_flv->c;
	int cts = 0;

	int64_t offset = 0;
	if (p_rtp->i_video_lasttime !=0)
	{
		offset = p_rtp->i_video_time_stamp - p_rtp->i_video_lasttime;
		if(p_flv->fps == 0 && offset != 0)
			p_flv->fps = p_rtp->rtptimebase/offset;
	}
	
	// A new frame - write packet header
	x264_put_byte(c, FLV_TAG_TYPE_VIDEO);
	x264_put_be24(c, 0); // calculated later
	//cts = (p_rtp->i_video_time_stamp - p_rtp->i_video_time_startoffset)/p_rtp->rtptimebase;
        cts = ((float)(p_rtp->i_video_time_stamp - p_rtp->i_video_time_startoffset)/p_rtp->rtptimebase)*1000;
		p_flv->lastfiletime = float(cts)/1000;
	printf("###### cts:%d\n", cts);
	x264_put_be24(c, cts);
	x264_put_byte(c, cts >> 24);
	x264_put_be24(c, 0);

	p_flv->start = c->d_cur;
	if (nalu.i_type == 5)
		x264_put_byte(c, FLV_FRAME_KEY);
	else x264_put_byte(c, FLV_FRAME_INTER);

	x264_put_byte(c, 1); // AVC NALU
	x264_put_be24(c, 0);//offset
	//x264_put_be16(c, 0);
	//x264_put_be32(c, nalu.i_payload );

	if (p_flv->sei)
	{
		flv_append_data(c, p_flv->sei, p_flv->sei_len);
		free(p_flv->sei);
		p_flv->sei = NULL;
	}
	flv_append_data(c, nalu.p_payload, nalu.i_payload );

	unsigned length = c->d_cur - p_flv->start;
	rewrite_amf_be24(c, length, p_flv->start - 10);
	x264_put_be32(c, 11 + length); // Last tag size
	CHECK(flv_flush_data(c));

	p_flv->i_framenum++;
	p_rtp->i_video_lasttime = p_rtp->i_video_time_stamp;
	return 0;
}
void rewrite_amf_uint64_t(FILE *fp, uint64_t position, uint64_t value)
{
	uint64_t x = endian_fix64(value);
	fseek(fp, position, SEEK_SET);
	fwrite(&x, 8, 1, fp);
}
void rewrite_amf_double(FILE *fp, uint64_t position, double value)
{
	uint64_t x = endian_fix64(dbl2int(value));
	fseek(fp, position, SEEK_SET);
	fwrite(&x, 8, 1, fp);
}

int close_file(flv_hnd_t *handle)
{
	flv_hnd_t *p_flv = handle;
	flv_buffer *c = p_flv->c;

	CHECK(flv_flush_data(c));

	if ((c->fp))
	{
		double total_duration;

		uint64_t filesize = ftell(c->fp);

		total_duration = p_flv->lastfiletime;//(double)p_flv->i_framenum / p_flv->fps;
		rewrite_amf_double(c->fp, p_flv->i_duration_pos, total_duration);
		rewrite_amf_double(c->fp, p_flv->i_filesize_pos, filesize);
		rewrite_amf_double(c->fp, p_flv->i_bitrate_pos, filesize * 8 / (total_duration * 1000));
		//rewrite_amf_uint64_t(c->fp, p_flv->i_firsttimestamp_pos, p_flv->i_video_time_startoffset);
		if(p_flv->fps)
			rewrite_amf_double( c->fp, p_flv->i_framerate_pos, p_flv->fps );
	}

	fclose(c->fp);
	free(p_flv);
	free(c);

	return 0;
}

int WriteF4v(char *infile, char *outfile)
{
	//int ret;
	//flv_hnd_t *p_handle;
	//x264_nal_t nalPacket[2];

	//if (FillBuffer(infile) == -1)
	//	return -1;
	//InitFirstPos();
	//printf("WriteF4v 1\n");
	//open_file(outfile, &p_handle);
	//printf("p_handle:%ld\n", p_handle);
	//printf("*p_handle:%ld\n", *p_handle);
	//printf("WriteF4v 2\n");
	//set_script_param(p_handle, 640, 480);
	//printf("WriteF4v 3\n");
	//ret = GetNalFromBuffer(&nalPacket[0]);
	//ret = GetNalFromBuffer(&nalPacket[1]);
	//write_h264_headers(p_handle, nalPacket);

	//while (ret == 0)
	//{
	//	ret = GetNalFromBuffer(&nalPacket[0]);
	//	//write_h264_frame(p_handle, nalPacket[0]);
	//}


	//close_file(p_handle);
	return 0;

}
/*
int close_file( flv_hnd_t *handle, int64_t largest_pts, int64_t second_largest_pts )
{
flv_hnd_t *p_flv = handle;
flv_buffer *c = p_flv->c;

CHECK( flv_flush_data( c ) );

double total_duration = (2 * largest_pts - second_largest_pts) * p_flv->d_timebase;

if(( c->fp ) && total_duration > 0 )
{
double framerate;
uint64_t filesize = ftell( c->fp );

if( p_flv->i_framerate_pos )
{
framerate = (double)p_flv->i_framenum / total_duration;
rewrite_amf_double( c->fp, p_flv->i_framerate_pos, framerate );
}

rewrite_amf_double( c->fp, p_flv->i_duration_pos, total_duration );
rewrite_amf_double( c->fp, p_flv->i_filesize_pos, filesize );
rewrite_amf_double( c->fp, p_flv->i_bitrate_pos, filesize * 8 / ( total_duration * 1000 ) );
}

fclose( c->fp );
free( p_flv );
free( c );

return 0;
}
*/
